#include <ucontext.h>
#include <stdio.h>
#include <errno.h>
#include <pthread.h>
#include <stdlib.h>
#include <uuid/uuid.h>
#include <unistd.h>
#include <setjmp.h>


#include "sysy_lib.h"
#include "buffer.h"

int  __pipe1__[2];
int  __pipe2__[2];

#define MAX_WORKER 10
int __epoll_fds__[MAX_WORKER];

static void *__worker_multi(void *arg)
{
        int ret, i, fd;
        struct epoll_event ev;
        struct timeval t1, t2;
        uint64_t used;
        int c;

        i = *(int *)arg;

        fd = epoll_create(1024);
        if (fd < 0) {
                ret = errno;
                UNIMPLEMENTED(__DUMP__);
        }

        __epoll_fds__[i] = fd;

        DINFO("create work %d\n", i);

        while (1) {
                _gettimeofday(&t1, NULL);

                DINFO("worker %u wait\n", i);

                ret = epoll_wait(__epoll_fds__[i], &ev, 1, -1);
                if (ret < 0) {
                        ret = errno;
                        if (ret == EINTR) {
                                DINFO("interrupted\n");
                                continue;
                        } else
                                UNIMPLEMENTED(__DUMP__);
                }

                _gettimeofday(&t2, NULL);

                used = _time_used(&t1, &t2);

                DINFO("worker %u weakup, got %u\n", i, ret);

                if (ret == 0) {
                        //usleep(1);
                        DINFO("worker %u get zero\n", i);
                        continue;
                }

                DBUG("used %llu\n", (LLU)used);

                ret = read(__pipe1__[0], &c, sizeof(c));
                if (ret != sizeof(c)) {
                        ret = errno;
                        YASSERT(0);
                }

                c = i;

                ret = write(__pipe2__[1], &c, sizeof(c));
                if (ret != sizeof(c)) {
                        ret = errno;
                        YASSERT(0);
                }

                //DINFO("worker %d read %u\n", *i, ret);

                //sleep(2);
        }

        return NULL;
}

static int __test_thread_multi(int *i)
{
        int ret;
        pthread_t th;
        pthread_attr_t ta;

        (void) pthread_attr_init(&ta);
        (void) pthread_attr_setdetachstate(&ta,PTHREAD_CREATE_DETACHED);

        ret = pthread_create(&th, &ta, __worker_multi, i);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

inline static int __test_epoll_mt()
{
        int ret, i, worker[MAX_WORKER];
        struct epoll_event ev;
        struct timespec t1, t2;
        uint64_t used;
        int c;

        DINFO("create socket\n");

        for(i = 0; i < MAX_WORKER; ++i){
                worker[i] = i;
                ret = __test_thread_multi(&worker[i]);
                if (unlikely(ret))
                        GOTO(err_ret, ret);
        }

        sleep(5);

        ret = pipe(__pipe1__);
        if (ret < 0) {
                ret = errno;
                GOTO(err_ret, ret);
        }

        ret = pipe(__pipe2__);
        if (ret < 0) {
                ret = errno;
                GOTO(err_ret, ret);
        }

        for(i = 0; i < MAX_WORKER; ++i) {
                ev.events = EPOLLIN | EPOLLET;
                ret = epoll_ctl(__epoll_fds__[i], EPOLL_CTL_ADD, __pipe1__[0], &ev);
                if (ret < 0) {
                        ret = errno;
                        GOTO(err_ret, ret);
                }

                DINFO("set worker %u\n", i);
        }


        while (1) {
                sleep(1);

                clock_gettime(CLOCK_REALTIME, &t1);

                ret = write(__pipe1__[1], &c, sizeof(c));
                YASSERT(ret == sizeof(c));

                ret = read(__pipe2__[0], &c, sizeof(c));
                if (ret != sizeof(c)) {
                        ret = errno;
                        YASSERT(0);
                }

                clock_gettime(CLOCK_REALTIME, &t2);

                used = _time_used1(&t1, &t2);

                DINFO("used %llu, from %u\n", (LLU)used / 1000, c);
        }

        return 0;
err_ret:
        return ret;
}

#if 0
void test1()
{
        printf("backtrace1\n");
        _backtrace();
        printf("backtrace2\n");
}

void test2()
{
        test1();
}

void test3()
{
        test2();
}

void test4()
{
        test3();
}
#endif

int main()
{
        //test4();
        __test_epoll_mt();

        return 0;
}
